class Inflex {
  url = `http://${window.location.hostname}:8086/query?pretty=false&epoch=ms&db=tw2_database&q=`

  execute(sql) {
    //pretty=true&
    let url = this.url + sql.replace(/[\s\n]+/g, ' ');
    console.log(url)
    return fetch(url).then(v => v.json())
    // 'query?pretty=true&db=mydb&q=SELECT%20value%20FROM%20cpu_load_short'
  }
}

function calculate(expression, vars) { // 使用 new Function 构造器创建一个函数
  let rst = NaN
  try{
    rst = new Function(...Object.keys(vars), `return ${expression};`)(...Object.values(vars));
  }catch (e){
    console.error(expression, vars, e)
  }
  return rst;
}

Array.prototype.sum = function () {
  return this.reduce((a, b) => a + b, 0)
}
let in2out = v => v;

function xnum(v) {
  return Number(v.split(':')[0])
}

function isNumInRanges(ranges) {
  t = function (v) {
    return ranges.some(range => v >= range[0] && v <= range[1]);
  }
  return t
}

class Param {
  _view = new DataView(new ArrayBuffer(8))

  constructor(d) {
    Object.assign(this, d)
    let In = d.In, tf = d.TF;
    this.Signbit = In.Signbit ? (In.Signbit * 1) : undefined;
    this.input = this[In.n] || in2out;
    if (d.RA) {
      let r_arr = d.RA.replace(/[\(\)\{\}\[\]]/g, '').split(',').map(r => Number(r));
      let ranges = Array.from({length: Math.ceil(r_arr.length / 2)},
        (_, i) => r_arr.slice(i * 2, i * 2 + 2));
      // console.log(d.n, ranges)
      this.RANGE = isNumInRanges(ranges)
    } else {
      this.RANGE = () => {
        return true
      }
    }

    // {"fn":"calibration01","n":"0.02V校准"}
    // {"CA":{"fn":"calibration02","n":"一般电平校准","calibrationUnit":"0.04394"}
    this.cali = d.CA ? this[d.CA.fn] : in2out;
    if (tf.fn === 'FuncExpr') {
      this.TF = (v, n) => calculate(tf.Coef, v);

    } else if (tf.fn === 'FuncIfelse') {
      // "Coef":"TMY408==0,TMY408=-273,TMY408=TMY408*2.4-300",
      // "Default":"TMY408=TMY408*2.4-300","Expression":"TMY408=-273"
      let exps = tf.Coef.split(',')
      let expr = `${exps[0]}?(${exps[1]}):(${exps[1]})`;
      this.TF = (v, n) => calculate(expr, v)
    } else {
      this.coef = eval(`[${tf.Coef}]`);
      this.coefParam = tf.CoefParam;
      let tfunc = this[tf.fn] || ((v, n) => v[n]);
      this.TF = (vals) => tfunc.bind(this)(vals, this.id, ...this.coef);
    }
    let pos = d.P.P;
    if (pos.constructor === String) pos = [pos];
    this.bits = pos.map(v => v.includes('.') ? 1 : 8).reduce((a, b) => a + b);
    if (d.PD) {
      this.tmap = {}
      if (d.PD.PDL.constructor === String) d.PD.PDL = [d.PD.PDL];
      d.PD.PDL.map(v => this.tmap[xnum(v)] = v)
    }
  }

  calibration01(v) {
    // "0.02V校准"
    return v * 0.02
  }

  calibration02(v) {
    // "一般电平校准","calibrationUnit":"0.04394"
    return v * this.CA.calibrationUnit;
  }

  Func0005(v, n, a) {
    return v[n] + a;
  }

  Func0006(v, n, a) {
    return v[n] - a;
  }

  Func0007(v, n, a) {
    return v[n] * a;
  }

  Func0008(v, n, a) {
    return v[n] / a;
  }

  Func0009(v, n, a) {
    return v[n] % a;
  }

  Func0010(v, n, k, b) {
    return v[n] * k + b;
  }

  Func0011(v, n, V, K, B) {
    let i = V.map(a => v[n] > a).reduce((a, b) => a + b, 0);
    return this.Func0010(v, n, K[i], B[i])
  }

  Func0012(v, n, V, C) {
    let i = V.slice(1, V.length - 1).map(a => v[n] > a).reduce((a, b) => a + b, 0);
    return ((V[i + 1] - v[n]) * C[i] + (v[n] - V[i]) * C[i + 1]) / (V[i + 1] - V[i]);
  }

  Func0013(v, n, ...arr) {
    let t = 0, x = 1, i;
    for (i = arr.length - 1; i >= 0; i--) {
      t += x * arr[i];
      x *= v[n];
    }
    return t;
  }

  Func0014(v, n, R1, Vs, a, b, c) {
    let R = R1 * Vs / v[n] - R1;
    return 2 * c / (-b + Math.sqrt(b ** 2 - 4 * c * (a - Math.log(R)))) - 273.15;
  }

  Func0015(v, n, R1, Vs, a, b, c) {
    let R = v[n] * R1 / (Vs - v[n]);
    if (R <= 0) return -Infinity;
    return 2 * c / (-b + Math.sqrt(b ** 2 - 4 * c * (a - Math.log(R)))) - 273.15;
  }

  Func0016(v, n, k1, b, k2) {
    let x = v[this.coefParam], y = v[n];
    return x * k1 + b - y * k2;
  }

  Func0017(v, n, k) {
    let tmk = v[this.coefParam], vl = v[n];
    return tmk ? (vl * k) : (-vl * k);
  }

  Func0018(v, n, k) {
    // todo 无18配置，不知道怎么写
  }

  FuncUD01(v, n, keys) {
    // todo 无18配置，不知道怎么写
  }

  F(v) {
    this._view.setUint32(0, v)
    return this._view.getFloat32(0)
  }

  D(v) {
    this._view.setBigUint64(0, v)
    return this._view.getFloat64(0)
  }

  I(v) {
    return (v >> this.Signbit) ? v - (1 << (this.Signbit + 1)) : v
  }
}


class PKG {
  // vals  ： 数据库查出来的
  // keys : 数据库里的 key
  // params : 参数解析方案
  constructor(params, keys, vals) {
    this.par = params
    this.input = {}
    this.raw = {}
    this.time = vals[0];
     keys.forEach((k, i) => {
      if (['time', 'station', 'track', 'arc', 'task','date'].includes(k)) return;
      let p = this.par[k];
      let r = this.raw[k] = vals[i]
      if (r !== null) this.input[k] = p.cali(p.input(r));
    })
    this.val = {}
  }

  // None
  V(name) {
    // 转换物理量
    if (!(name in this.input)) return undefined
    if (this.val[name]) return this.val[name]
    let par = this.par[name];
    return this.val[name] = par.TF(this.input)
  }

  R(name) {
    // 用于源码显示
    if (!(name in this.input)) return ''
    let p = this.par[name];
    if (p.bits % 8) return '0b' + this.raw[name].toString(2).padStart(p.bits, '0');
    return '0x' + this.raw[name].toString(16).padStart(p.bits / 4, '0').toUpperCase();
  }

  D(name) {
    if (!(name in this.input)) return '-'
    let p = this.par[name]
    let v = this.V(name);
    switch (true) {
      case p.tmap && true:
        v = p.tmap[v] || `${v}:未定义/${p.tmap}`;
        break;
      case p.PD && p.PD.HEX === 16:
        v = '0x' + v.toString(16).padStart(p.PD.showlength, '0').toUpperCase();
        break;
      case p.PD && p.PD.HEX === 10:
        v = v.toFixed(p.PD.showlength || 4);
        break;
      default:
        if(v==undefined) return ''
        v = v.toFixed(3)
    }
    return v
  }

  td(name) {
    let p = this.par[name]
    let v = this.V(name);
    let st = (name in this.input) && p.RANGE(v) ? '' : 'class="E"'
    return `<td ${st}>${this.D(name)}${p.Unit || ''}  <kbd>${this.R(name)}</kbd></td>`
  }


  tr(names) {
    let tmp = names.map(name => this.td(name)).join('')
    return `<tr><th>${U2T(this.time)}</th>${tmp}</tr>`
  }

  grid_item(name) {
    let p = this.par[name]
    let v = this.V(name);
    let st = p.RANGE(v) ? '' : 'E'
    return `<div class="grid-inner ${st}" >
      <b>${p.n}</b>
      <i>${p.id}</i>
      <v>${this.D(name)}${p.Unit || ''}</v>
      <kbd>${this.R(name)}</kbd></div>`
  }

  grid(names) {
    let tmp = names.map(name => this.grid_item(name)).join('')
    return `<div class="grid-outer">${tmp}</div>`
  }
}

// 修复时区转换：统一使用北京时间
U2T = (ms) => {
  // 将UTC时间转换为北京时间显示
  const bjt = new Date(ms + 8 * 60 * 60 * 1000);
  return bjt.toISOString();
}

class PKGS {
  constructor(config) {
    Object.assign(this, config);
    this.keys = []
    this.par = {}
    this.TmParas.map(p => {
      this.par[p.id] = new Param(p);
      this.keys.push(p.id)
    });
  }

  data(name) {
    let meta = this.par[name];
    return {
      info: this.par[name],
      name: meta.n,
      // type: 'scatterGL',
      type: 'scatter',
      // type: 'line3D',
      // type: 'line',
      large: true,
      symbolSize: 3, symbol: 'rect',
      data: (this.values || []).map(v => [v.time, v.V(name)])
    }
  }

  th(name) {
    return `<th>${this.par[name].n}</th>`;
  }

  thead(names) {
    return `<tr><th>时间</th>${names.map(v => this.th(v)).join('')}</tr>`;
  }

  dataset(data) {
    let d = data.results[0].series
    this.values = [];
    if (d) {
      d = d[0];
      let keys = d.columns.map(v => v.replace(/.+?st_/, ''))
      this.values = d.values.filter(v => v[1] !== null).map(v => new PKG(this.par, keys, v))
      return this.values
    }
  }

  to_csv(names, obj) {
    obj.disabled = true
    try {

       let title = names.map(k => {
        let p = this.par[k];
        return `${p.n.replace(/(^[+,\-,=,*])/,' $1')},${p.id}`
      }).join(',')
      let data = [new Uint8Array([0xEF, 0xBB, 0xBF]), `时间,${title}\n`];
      this.values.forEach(v => {
        let line = names.map(n => `${v.D(n)},${v.R(n)}`).join(',')
        data.push(`${U2T(v.time)},${line}\n`)
      })
      let blob = new Blob(data, {'content_type': 'application/csv;charset=utf8'});
      let a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      let st = $TS.value.replace(/[-,T,:]/g,'')
      let et = $TE.value.replace(/[-,T,:]/g,'')
      a.download = `工程遥测_BACC_${current_packages.n}_${st}_${et}.csv`
      a.click()
      setTimeout(() => {
        URL.revokeObjectURL(a.href)
      }, 3000)
    } finally {
      obj.disabled = false;
    }
  }
}

